#include "mysql_connection_pool.h"
#include <iostream>
#include <stdexcept>

MySQLConnection::MySQLConnection(const DatabaseInfo& dbInfo)
    : mysql(nullptr), result(nullptr), dbInfo(dbInfo), connected(false) {
    mysql = mysql_init(nullptr);
    if (!mysql) {
        throw std::runtime_error("MySQL initialization failed");
    }
}

MySQLConnection::~MySQLConnection() {
    if (result) {
        mysql_free_result(result);
    }
    if (mysql) {
        mysql_close(mysql);
    }
}

bool MySQLConnection::connect() {
    if (mysql_real_connect(mysql, dbInfo.host.c_str(), dbInfo.user.c_str(),
                          dbInfo.password.c_str(), dbInfo.database.c_str(),
                          dbInfo.port, nullptr, 0)) {
        connected = true;
        return true;
    }
    connected = false;
    return false;
}

bool MySQLConnection::isValid() const {
    return connected && mysql_ping(mysql) == 0;
}

bool MySQLConnection::execute(const std::string& sql) {
    if (!isValid()) {
        return false;
    }
    
    return mysql_query(mysql, sql.c_str()) == 0;
}

bool MySQLConnection::executeQuery(const std::string& sql) {
    if (!isValid()) {
        return false;
    }
    
    if (mysql_query(mysql, sql.c_str()) != 0) {
        return false;
    }
    
    result = mysql_store_result(mysql);
    return result != nullptr;
}

MYSQL_RES* MySQLConnection::getResult() {
    return result;
}

char* MySQLConnection::getField(const std::string& fieldName) {
    if (!result) {
        return nullptr;
    }
    
    MYSQL_ROW row = mysql_fetch_row(result);
    if (!row) {
        return nullptr;
    }
    
    MYSQL_FIELD* fields = mysql_fetch_fields(result);
    unsigned int num_fields = mysql_num_fields(result);
    
    for (unsigned int i = 0; i < num_fields; i++) {
        if (fieldName == fields[i].name) {
            return row[i];
        }
    }
    
    return nullptr;
}

void MySQLConnection::freeResult() {
    if (result) {
        mysql_free_result(result);
        result = nullptr;
    }
}

MySQLConnectionPool* MySQLConnectionPool::getInstance() {
    static MySQLConnectionPool instance;
    return &instance;
}

MySQLConnectionPool::MySQLConnectionPool()
    : maxPoolSize(0), initialized(false) {
}

MySQLConnectionPool::~MySQLConnectionPool() {
    destroyPool();
}

bool MySQLConnectionPool::initPool(const DatabaseInfo& dbInfo, int poolSize) {
    if (initialized) {
        return true;
    }
    
    this->dbInfo = dbInfo;
    this->maxPoolSize = poolSize;
    
    std::lock_guard<std::mutex> lock(poolMutex);
    
    for (int i = 0; i < poolSize; i++) {
        MySQLConnection* conn = new MySQLConnection(dbInfo);
        if (conn->connect()) {
            connectionPool.push(conn);
        } else {
            delete conn;
            return false;
        }
    }
    
    initialized = true;
    return true;
}

std::shared_ptr<MySQLConnection> MySQLConnectionPool::getConnection() {
    std::unique_lock<std::mutex> lock(poolMutex);
    
    while (connectionPool.empty()) {
        poolCondition.wait(lock);
    }
    
    MySQLConnection* conn = connectionPool.front();
    connectionPool.pop();
    
    // Check if connection is still valid
    if (!conn->isValid()) {
        // Try to reconnect
        if (!conn->connect()) {
            // If reconnect fails, create a new connection
            delete conn;
            conn = new MySQLConnection(dbInfo);
            if (!conn->connect()) {
                connectionPool.push(conn);
                poolCondition.notify_one();
                return nullptr;
            }
        }
    }
    
    // Return a shared_ptr with custom deleter to return connection to pool
    return std::shared_ptr<MySQLConnection>(conn, [this](MySQLConnection* c) {
        std::lock_guard<std::mutex> lock(this->poolMutex);
        this->connectionPool.push(c);
        this->poolCondition.notify_one();
    });
}

void MySQLConnectionPool::releaseConnection(MySQLConnection* conn) {
    if (conn) {
        std::lock_guard<std::mutex> lock(poolMutex);
        connectionPool.push(conn);
        poolCondition.notify_one();
    }
}

void MySQLConnectionPool::destroyPool() {
    std::lock_guard<std::mutex> lock(poolMutex);
    
    while (!connectionPool.empty()) {
        MySQLConnection* conn = connectionPool.front();
        connectionPool.pop();
        delete conn;
    }
    
    initialized = false;
}